home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fritz: All Fritz
/
All Fritz.zip
/
All Fritz
/
FILES
/
EDUCNOMY
/
HERSCHEL.LZH
/
HBASE.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-12-26
|
49KB
|
1,123 lines
Program HerschelCatalogDataBase (Input,Output);
{ This program is a simple data base manager for the Herschel catalog of
deep sky objects, for amateur astronomers. }
{$C-} { No user breaks - to speed screen output }
Const
NumberOfRecords = 2510; { Number of records currently in the data file }
NumberOfConstellations = 88; { Serpens is treated as a single constellation }
Heading : String[70] = { The typed constant generates less object code }
' H Class RNGC R.A. Dec. Mag. Type Const';
Type
HRecord = Record { The main record description used throughout the program }
HClass : Byte; { Byte types are used to save file space }
HNum : Integer; { But some fields go over the 0..255 limit }
NGC : Integer;
RAHrs : Byte;
RAMins : Byte;
RASecs : Byte;
DecDeg : Integer;
DecMin : Integer; { A neg. DecMin value is used to indicate }
Mag : Byte; { objects of Dec. 0d.,Xm. which are south }
Class : Byte; { of the equator by X mins. (needed 'cause }
Con : Byte; { you can't have a DecDeg integer with a }
End; { Record } { value of -0 }
{ The following structure is used to build a linked-list which holds the
entire data file while the program runs. This linked structure is used
because there is not enough memory left in the data segment for an array. }
HRecordPointer = ^HElement;
HElement = Record
Data : HRecord;
Next : HRecordPointer;
End; { Record }
{ The following record is used for calling DOS interrupts }
Register = Record
AX,BX,CX,DX,Bp,SI,DI,DS,ES,Flags: Integer;
End; { Record }
HClassSet = Set Of 1..8; { Set used in selecting H classes }
HTypeSet = Set Of 1..7; { Set used in selecting object types }
Cons = 0..NumberOfConstellations; { Range of constellation indices }
ConNames = Array[Cons] Of String[3];
{ ConNames is the type description of the "Names" typed constant below }
Types = Array[1..7] Of String[16];
{ Types is the type description of the "TypeNames" typed constant below }
Classes = Array[1..8] Of String[4];
{ Classes is the type description of the "TypeNames" typed constant below }
ObjectType = Array[1..7] Of Char;
{ ObjectType is the type description of the "ObjectTypes" typed constant }
HArray = Array[1..NumberOfRecords] Of HRecord;
{ Misc. typed constants follow }
Const
{ Typed constant array of constellation names (official abbreviations) }
Names : ConNames=(' ','And','Ant','Aps','Aqr','Aql','Ara','Ari','Aur','Boo',
'Cae','Cam','Cnc','CVn','CMa','CMi','Cap','Car','Cas','Cen','Cep','Cet',
'Cha','Cir','Col','Com','CrA','CrB','Crv','Crt','Cru','Cyg','Del','Dor',
'Dra','Equ','Eri','For','Gem','Gru','Her','Hor','Hya','Hyi','Ind','Lac',
'Leo','LMi','Lep','Lib','Lup','Lyn','Lyr','Men','Mic','Mon','Mus','Nor',
'Oct','Oph','Ori','Pav','Peg','Per','Phe','Pic','Psc','PsA','Pup','Pyx',
'Ret','Sge','Sgr','Sco','Scl','Sct','Ser','Sex','Tau','Tel','Tri','TrA',
'Tuc','UMa','UMi','Vel','Vir','Vol','Vul');
{ Names of object types used for display }
TypeNames : Types = ('Open Cluster ','Globular Cluster',
'Diffuse Nebula ','Planetary Nebula','Galaxy ','Cluster/Nebula ',
'Nonexistant ');
{ Object type abbreviations used for display }
ObjectTypes : ObjectType = ('O','C','D','P','G','/','N');
{ Typed constant array of Herschel classes in Roman numeral form }
ClassNames : Classes = (' I',' II',' III',' IV',' V',' VI',
' VII','VIII');
Var { Misc. global variables }
FirstPosition,CurrentPosition : HRecordPointer;
SelectArray : HArray; { User's selected data }
SelectPointer,LowNGC,HighNGC,LowDecDeg,EndOfArray,VideoOfs,
HighDecDeg,LowDecMin,HighDecMin,InCount,Index : Integer;
LowRAHr,HighRAHr,LowRAMin,HighRAMin,Row,Col : Byte;
LowMag,HighMag,CurrentEpoch,StartTime,FinishTime : Real;
Constel,TrueConArray : Array[Cons] Of Boolean;
{ The Constel array flags each constellation as selected or not }
Object : HRecord; { The variable used to hold the current record }
SortField,Ch : Char; { Variables used for reading key presses }
OK,AllOK,EndOfInput,Done,Selected,NewSelection,Expanding : Boolean;
Device : Text; { Procedure WriteALine writes to this file (screen or print) }
ClassSet : HClassSet; { These sets are used in the selection process }
TypeSet : HTypeSet;
Procedure MemoryWrite(Ch: Char);
{ This procedure is a user-written I/O driver for screen output. It writes
output directly to screen memory. This makes screen output much faster.
Procedure View activates this driver, and de-activates it before returning
to the main menu. }
Const
VideoSeg = $B000; { Video memory segment address }
Var
SChar : Integer;
Begin { Procedure MemoryWrite }
If Ch = #13 Then { Test for carriage return }
Begin { Then }
Row := Succ(Row); { Adjust row & col for new line }
Col := 0;
End { Then }
Else
Begin { Else }
Col := Succ(Col); { New column for each character }
SChar := ((Row-1)*160) + ((Col-1)*2); { Compute starting location }
Mem[VideoSeg:VideoOfs + SChar] := Ord(Ch); { Put character in memory }
End; { Else }
End; { Procedure MemoryWrite }
{$I SORT.BOX} { Include Borland's SORT.BOX toolbox include file }
Procedure ReadList;
{ This procedure supplies a single record of input to procedure Inp (below).
ReadList reads from the static linked list containing the data file. This
is the normal source of input when the program begins, or after the user
has done an initialize. }
Begin { Procedure ReadList }
Object := CurrentPosition^.Data;
CurrentPosition := CurrentPosition^.Next;
EndOfInput := CurrentPosition = Nil;
End; { Procedure ReadList }
Procedure ReadArray;
{ This procedure supplies a single record of input to procedure Inp (below).
ReadArray reads from the SelectArray, which contains the user's currently
selected data. This array is the source of input whenever the user does a
second select-and-sort without first reinitializing. }
Begin { Procedure ReadArray }
Object := SelectArray[SelectPointer];
SelectPointer := Succ(SelectPointer);
EndOfInput := SelectPointer > EndOfArray;
End; { Procedure ReadArray }
Procedure Inp;
{ This procedure is called by the Borland sort routines. This is where
the program compares each object in the select array against the values
chosen by the user, creating selected input to the sort. }
Begin { Procedure Inp }
SelectPointer := 1; { See proc. ReadArray }
CurrentPosition := FirstPosition; { See proc. ReadList }
EndOfInput := False; { For procs. ReadList & ReadArray }
EndOfArray := InCount; { For proc. ReadArray }
InCount := 0; { Var. to keep track of # of currently selected objects }
Writeln('Reading and selecting input data'); { Look familiar? }
Repeat { Loop to extract all valid input to sort }
If Not Selected Then { All new input required - get it from linked list }
ReadList
Else { Else we are selecting from pre-sorted data - use SelectArray }
ReadArray;
With Object Do
Begin { With }
If { <- This if statement is the heart }
Constel[Con] Then If { of the program. It does the actual}
(Class In TypeSet) Then If { comparing in the selection process}
(NGC >= LowNGC) Then If { The use of "Then If's" rather than}
(NGC <= HighNGC) Then If { "And's" speeds the comparison }
(RAHrs > LowRAHr) Or { process by eliminating further }
((RAHrs = LowRAHr) And { comparison as soon as a boolean }
(RAMins >= LowRAMin)) Then If { test fails. }
(RAHrs < HighRAHr) Or { We test in order of likelyhood - }
((RAHrs = HighRAHr) And { by const., then object type etc. }
(RAMins <= HighRAMin)) Then If
(DecDeg > LowDecDeg) Or
((DecDeg = LowDecDeg) And
(DecDeg >= 0) And
(DecMin >= LowDecMin)) Or { See comment in Object type desc. }
((DecDeg = LowDecDeg) And { concerning neg. DecMin values }
(DecDeg < 0) And
(DecMin <= LowDecMin)) Then If
((DecDeg < HighDecDeg) Or
((DecDeg = HighDecDeg) And
(DecMin <= HighDecMin))) Then If
(Mag >= LowMag) Then If
(Mag <= HighMag) Then If
(HClass In ClassSet)
Then
Begin { Then }
SortRelease(Object); { Release object to Borland's sort }
InCount := Succ(InCount); { Keep count of objects selected }
End; { Then }
End; { With }
Until EndOfInput;
Selected := True; { User is creating a select array so its ok to precess }
Writeln(Incount,' records input to sort');
Writeln('Sorting'); { You'll stare at this line during the actual sort }
End; { Procedure Inp }
Procedure Outp;
{ This procedure takes the output from the sort and writes it to the select
array. This is the array on which all further operations will operate,
until the user "initializes". This procedure is called from Borland's sort
routines. }
Begin { Procedure Outp }
Writeln('Writing selected output data'); { The last sort screen message }
For Index := 1 To Incount Do
Begin { For }
SortReturn(Object); { Return records in order from Borland's sort }
SelectArray[Index] := Object; { Put 'em in the SelectArray }
End; { For }
End; { Procedure Outp }
Function Less; { Foward declared from the include file as type boolean }
{ This procedure is called by the Borland sort routines. Here is where the
actual comparison process for the sort takes place. The case statement
controls the fields sorted on, depending on the user's choice. }
Var
FirstObject : HRecord Absolute X; { Records are passed to Borland's sort }
SecondObject : HRecord Absolute Y; { by these absolute variables. }
Begin { Function Less }
Case SortField Of { The same char. the user asked for in proc. Sort }
'H','h' : Less := (FirstObject.HClass < SecondObject.HClass) Or
((FirstObject.HClass = SecondObject.HClass) And
(FirstObject.HNum < SecondObject.HNum));
'N','n' : Less := FirstObject.NGC < SecondObject.NGC;
'R','r' : Less := (FirstObject.RAHrs < SecondObject.RAHrs) Or
((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObJect.RAMins < SecondObject.RAMins)) Or
(((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObject.RAMins = SecondObject.RAMins) And
(FirstObject.RASecs < SecondObject.RASecs)));
'D','d' : Less := (FirstObject.DecDeg < SecondObject.DecDeg) Or
((FirstObject.DecDeg = SecondObject.DecDeg) And
(FirstObject.DecDeg < 0) And
(FirstObject.DecMin > SecondObject.DecMin)) Or
((FirstObject.DecDeg = SecondObject.DecDeg) And
(FirstObject.DecDeg >= 0) And
(FirstObject.DecMin < SecondObject.DecMin));
'M','m' : Less := (FirstObject.Mag < SecondObject.Mag) Or
((FirstObject.Mag = SecondObject.Mag) And
((FirstObject.RAHrs < SecondObject.RAHrs) Or
((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObJect.RAMins < SecondObject.RAMins)) Or
(((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObject.RAMins = SecondObject.RAMins) And
(FirstObject.RASecs < SecondObject.RASecs)))));
'O','o' : Less := (FirstObject.Class < SecondObject.Class) Or
((FirstObject.Class = SecondObject.Class) And
((FirstObject.RAHrs < SecondObject.RAHrs) Or
((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObJect.RAMins < SecondObject.RAMins)) Or
(((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObject.RAMins = SecondObject.RAMins) And
(FirstObject.RASecs < SecondObject.RASecs)))));
'C','c' : Less := (FirstObject.Con < SecondObject.Con) Or
((FirstObject.Con = SecondObject.Con) And
((FirstObject.RAHrs < SecondObject.RAHrs) Or
((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObJect.RAMins < SecondObject.RAMins)) Or
(((FirstObject.RAHrs = SecondObject.RAHrs) And
(FirstObject.RAMins = SecondObject.RAMins) And
(FirstObject.RAsECS < SecondObject.RASecs)))));
End; { Case }
End; { Function Less }
Procedure Error(ErrorNumber,ErrorAddress : Integer);
{ This procedure is a user written error handler. }
{ It will execute if an error occurs. }
Begin { Procedure Error }
ClrScr;
Writeln('HBASE has crashed.'); { In case the user hadn't noticed }
If (Hi(ErrorNumber) = 2) And (Lo(ErrorNumber) = $FF) Then
Begin { Then }
Writeln('Insufficient memory for execution.');
Writeln('Remove any memory resident software and try again.');
End; { Then }
If (Hi(ErrorNumber) = 1) And (Lo(ErrorNumber) = 1) Then
Writeln('File HBASE.DAT must be in current directory of default drive!');
Halt; { Stop the program "manually" after reporting error }
End; { Procedure Error }
Procedure InitializeVariables;
{ This procedure initializes various variables to their origional state. It
is called from procedure Initialize when the program first begins, and is
also the procedure called by the initialize option from the main menu. }
Begin { Procedure InitializeVariables }
Expanding := False; { Here is where it is reset }
NewSelection := False; { Nothing has been selected }
Selected := False; { No data selected yet - can't precess }
InCount := 0; { Nothing has been selected & sorted yet }
CurrentEpoch := 1975.0; { The epoch of the data file }
{ The rest of the statements assign values to the selection variables that
will select for all possible objects. Thus, you get everything until you
narrow down these values in the selection procedures. }
SortField := ' ';
ClassSet := [1..8];
TypeSet := [1..7];
LowNGC := 0;
HighNGC := 8000;
LowRAHr := 0;
HighRAHr := 24;
LowRAMin := 0;
HighRAMin := 0;
LowDecDeg := -90;
HighDecDeg := 90;
LowDecMin := 0;
HighDecMin := 0;
LowMag := 0.0;
HighMag := 170.0;
Constel := TrueConArray; { Each element = true, all const. selected }
End; { Procedure InitializeVariables }
Procedure Tab(NumberOfSpaces : Byte);
{ Tab over a number of spaces rather than writing space constants }
Begin { Procedure Tab }
GoToXY(WhereX + NumberOfSpaces,WhereY);
End; { Procedure Tab }
Procedure WriteTitleScreen;
{ Please leave this in place - I don't ask for money - just my name in lights }
Begin { Procedure WriteTitleScreen }
Clrscr;
Writeln; Writeln; Writeln;
Tab(32); Writeln('║ ║ ');
Tab(32); Writeln('╠═══╣ ');
Tab(32); Writeln('║ ║ B A S E');
Writeln; Writeln; LowVideo;
Tab(13); Writeln('A project in amateur astronomy by G. Dean Williams');
Writeln;
Tab(14); Writeln('Data by Sir William Herschel and Dennis Donnelly');
GoToXY(67,25); Write('Version 01/87');
End; { Procedure WriteTitleScreen }
Procedure GetScreenType;
{ This procedure determines whether the system uses a monochrome or color
screen. This information is needed in procedure memorywrite. }
Var
Registers : Register;
Result : Integer;
ScreenType : Byte;
Begin { Procedure GetScreenType }
INTR($11,Registers); { Interrupt to return screen type }
Result := Registers.AX; { The raw result is in register AX }
ScreenType := (Result Shl 10 ) Shr 14; { Extract screen type from result }
If ScreenType = 2 Then
VideoOfs := $8000 { Color system }
Else
VideoOfs := $0000; { Monochrome system }
End; { Procedure GetScreenType }
Function Time: Real; { Get system time for calculating program run time }
Var
RecPack: Register;
Ah,Al,Ch,Cl,Dh : Byte;
Begin { Function Time }
Ah := $2c; { Initial vaule before DOS call }
With RecPack Do
Begin { With }
Ax := Ah Shl 8 + Al; { Prepare register value for interrupt }
End; { With }
Intr($21,RecPack); { Ask DOS for the time }
With RecPack Do { Calculate time in seconds }
Time := (Cx Shr 8) * 3600.0 + (Cx Mod 256) * 60.0 + (Dx Shr 8);
End; { Function Time }
Procedure Initialize;
{ This procedure is called from the main program when the program starts.
It initializes a few necessary variables. }
Var
InFile : File Of HArray;
ConIndex : Byte;
Begin { Procedure Initialize }
StartTime := Time; { Used to calculate program run time }
WriteTitleScreen;
ErrorPtr := Ofs(Error); { Activate the error handler procedure }
AuxOutPtr := ConOutPtr; { Save ConOutPtr }
GetScreenType; { Monochrome or color system? }
Done := False; { See main program block }
For ConIndex := 0 To NumberOfConstellations Do { Select all cons }
TrueConArray[ConIndex] := True;
InitializeVariables;
Assign(InFile,'HBASE.DAT');
Reset(Infile); { It had better be there or we'll crash }
Read(InFile,SelectArray); { Load the select array with one big disk read }
Close(InFile);
New(FirstPosition); { Starting place for linked list }
CurrentPosition := FirstPosition; { Start at the start }
For Index := 1 To NumberOfRecords Do
Begin { For loop to load the linked list from the select array }
CurrentPosition^.Data := SelectArray[Index]; { Load object to list }
New(CurrentPosition^.Next); { Increment position in liked list }
CurrentPosition := CurrentPosition^.Next; { Increment CurrentPosition }
End; { For }
CurrentPosition^.Next := Nil; { The last linked list entry points nowhere }
InCount := NumberOfRecords; { All objects are selected & sorted by H # }
End; { Procedure Initialize }
Procedure WaitForSpace; { Wait until user presses space bar }
Begin { Procedure WaitForSpace }
Repeat
Read(Kbd,Ch);
Until Ch = ' ';
End; { Procedure WaitForSpace }
Procedure Sort;
{ This procedure contains the sort menu, chosen from the main menu }
Var
SortFieldHold : Char;
SortResult : Integer;
Begin { Procedure Sort }
ClrScr;
Window(20,1,80,25); { Center sort menu screen }
Ch := 'Y'; { In case we skip the following read }
If ((InCount >= NumberOfRecords) And (Not NewSelection))
Or (Not (Selected Or NewSelection) And (InCount = 0)) Then
Begin { Then }
Writeln;
Write('Really sort the entire catalog? (Y/N): ');
Repeat { Outer loop to catch those damn escape codes }
Repeat
Read(Kbd,Ch);
Until Upcase(Ch) In ['Y','N',#27];
If (Ch = #27) And Keypressed Then { Trap escape codes }
Begin { Then }
Read(Kbd,Ch); { Read 2nd char of escape code }
Ch := 'a'; { Look out for escape code with "y" or "n" }
End; { Then }
Until Upcase(Ch) In ['Y','N'];
Writeln(Ch);
End; { Then }
If Upcase(Ch) = 'Y' Then { We are going to sort - proceed }
Begin { Then }
{ The next line saves SortField in case it gets clobbered with "Q" }
SortFieldHold := SortField;
Writeln;
Writeln('You can sort on the following fields:');
Writeln;
HighVideo; Write(' H'); LowVideo; Writeln('erschel Class');
HighVideo; Write(' N'); LowVideo; Writeln('GC Number');
HighVideo; Write(' R'); LowVideo; Writeln('ight Ascension');
HighVideo; Write(' D'); LowVideo; Writeln('eclination');
HighVideo; Write(' M'); LowVideo; Writeln('agnitude');
HighVideo; Write(' O'); LowVideo; Writeln('bject Type');
HighVideo; Write(' C'); LowVideo; Writeln('onstellation');
Writeln;
Write('Type a letter to sort or "Q" to quit to previous screen: ');
Repeat { Outer loop to catch unwanted escape codes }
Repeat
Read(Kbd,SortField);
Until Upcase(SortField) In ['H','N','R','D','M','O','C','Q',#27];
If (SortField = #27) And Keypressed Then { Trap out escape codes }
Begin { Then }
Read(Kbd,SortField); { Get 2nd char of escape code }
SortField := 'a'; { So it isn't a sort field character }
End; { Then }
Until Upcase(SortField) In ['H','N','R','D','M','O','C','Q'];
HighVideo; Writeln(SortField); LowVideo;
Writeln;
If Upcase(SortField) <> 'Q' Then { A real sort field was entered }
Begin { Then }
NewSelection := False; { Selections are being sorted }
{ The call to the actual sort is in the next line }
SortResult := TurboSort(SizeOf(HRecord)); { Call sort function }
If SortResult <> 0 Then { Report sort error }
Begin { Then }
Writeln('--- Error Occured During Sort ---');
Case SortResult Of
3 : Writeln('Not enough free memory for sorting');
10,11 : Writeln('Probable disk I/O error or disk full');
12 : Writeln('Disk directory full');
End; { Case SortResult }
Write('Press Space To Continue');
WaitForSpace;
End; { Then }
End { Then }
Else { The user did a "Q", so restore SortField }
SortField := SortFieldHold;
End; { Then }
End; { Procedure Sort }
Procedure WriteALine;
{ This procedure writes a single line of output, either to the screen,
or to the printer. It is called by procedures List and View. }
Var
RealMag : Real;
Begin { Procedure WriteALine }
With Object Do
Begin { With }
Write(Device,' ',ClassNames[HClass],'-');
{ Classnames are the roman numeral classes stored in ClassNames array }
If HNum < 10 Then { We must test for & print all leading zeros so }
Write(Device,'00') { that all field columns line up evenly. }
Else
If HNum < 100 Then
Write(Device,'0');
Write(Device,HNum,' ');
If NGC < 10 Then
Write(Device,'000')
Else
If NGC < 100 Then
Write(Device,'00')
Else
If NGC < 1000 Then
Write(Device,'0');
Write(Device,NGC,' ');
If RAHrs < 10 Then
Write(Device,'0');
Write(Device,RAHrs,'/');
If RAMins < 10 Then
Write(Device,'0');
Write(Device,RAMins,'/');
If RASecs < 10 Then
Begin { Then }
Write(Device,'0');
Write(Device,RASecs:1,' ');
End { Then }
Else
Write(Device,RASecs:2,' ');
If (DecDeg < 0) Or (DecMin < 0) Then
Write(Device,'-')
Else
Write(Device,' ');
DecDeg := Abs(DecDeg); { We print neg. sign manually }
If Decdeg < 10 Then
Begin { Then }
Write(Device,'0');
Write(Device,DecDeg,'/');
End { Then }
Else
Write(Device,DecDeg,'/');
DecMin := Abs(DecMin); { See note in HRecord type description }
If DecMin < 10 Then
Write(Device,'0');
Write(Device,DecMin,' ');
RealMag := Mag;
RealMag := RealMag / 10; { Magnitudes are all multiplied by 10 so }
If RealMag < 10 Then { they can be stored as bytes & save space }
Begin { Then }
Write(Device,'0');
Write(Device,RealMag:3:1,' ');
End { Then }
Else
Write(Device,RealMag:4:1,' ');
Write(Device,TypeNames[Class]);
Write(Device,' ',Names[Con],#13); { Write carriage return at end }
End; { With }
End; { Procedure WriteALine }
Procedure List;
{ This procedure sends the selected data to the printer }
Const
FormFeed = #12;
Var
NumberOfReports,CopyCount,LineCount : Byte;
Begin { Procedure List }
If NewSelection Then
Sort; { User cannot list data until it is sorted }
Assign(Device,'Lst:'); { So WriteALine will write to printer }
Reset(Device);
If InCount > 0 Then { There is something selected to print }
Begin { Then }
NumberOfReports := 1; { Default so user can just hit enter for 1 }
Writeln;
Repeat { Loop to get # of listings }
Write('Enter desired number of copies (default is 1): ');
{$I-} Readln(NumberOfReports) {$I+};
OK := (IoResult = 0) And (NumberOfReports > 0) And
(NumberOfReports < 251);
If Not OK Then
Write(^G); { Ring bell to alert user to entry error }
If NumberOfReports > 250 Then
Writeln('The maximum number of listings is 250!');
Writeln;
Until OK;
ClrScr;
Writeln('Ready printer and press space to proceed');
Writeln('You can type "Q" at any time to stop printing ');
WaitForSpace;
CopyCount := 0;
Repeat { Loop for number of copies }
CopyCount := Succ(CopyCount);
GotoXY(1,4); { So object count will stay put between copies }
Writeln('List of selected objects going to printer.');
For LineCount := 1 To 3 Do
Writeln(Lst);
Writeln(Lst,' ',Heading);
Writeln(Lst);
LineCount := 5;
SelectPointer := 0;
Repeat { Loop for writing all selected objects }
SelectPointer := Succ(SelectPointer);
GoToXY(1,5); { Position for the following write statements }
Write(Succ(InCount) - SelectPointer,' objects left to print');
Write(' on copy ',CopyCount,' of ',NumberOfReports,'. ');
If KeyPressed Then
Begin { Then }
Read(Kbd,Ch); { Get the character }
If (Ch = #27) And Keypressed Then { Extended scan code? }
Begin { Then }
Read(Kbd,Ch); { Get 2nd character of scan code }
Ch := 'a'; { Trap unwanted "Q"s }
End; { Then }
If Upcase(Ch) = 'Q' Then
SelectPointer := Incount; { Skip to end of list to stop }
End; { Then }
Object := SelectArray[SelectPointer]; { Get object to print }
WriteALine; { Write it to Lst: device }
Write(Lst,#10); { Line feed after carriage return from WriteALine }
LineCount := Succ(LineCount); { Keep count of print lines }
If LineCount > 62 Then { Time for new page }
Begin { Then }
Write(Lst,FormFeed); { Form Feed At End Of Each Page }
For LineCount := 1 To 3 Do
Writeln(Lst);
Writeln(Lst,' ',Heading);
Writeln(Lst);
LineCount := 5; { Adjust for heading lines }
End; { Then }
Until SelectPointer = Incount; { The last object }
Write(Lst,FormFeed); { Final form feed between copies }
Until (CopyCount = NumberOfReports) Or (Upcase(Ch) = 'Q');
End { Then }
Else
Begin { Else }
Write('No objects to list - Press Space To Continue ');
WaitForSpace;
End; { Else }
End; { Procedure List }
Procedure View;
{ This procedure contains the on-screen editor code }
Const
Escape = #27;
Var
PagePointer : Integer;
MaxDetailLines,Count : Byte;
FunKey,TopOfList,BottomOfList : Boolean;
Procedure WriteScreen;
{ This procedure is contained in, and is called by procedure view. It's
purpose is to write a screenful of output on the screen. }
Begin { Procedure WriteScreen }
ClrScr;
MaxDetailLines := 23; { Maximum detail lines on the view screen }
TopOfList := (PagePointer = 0);
If TopOfList Then { Write ** Top Of List ** message at top }
MaxDetailLines := Pred(MaxDetailLines); { Leave space for message }
BottomOfList := (InCount - PagePointer < MaxDetailLines);
If BottomOfList Then { Write ** Bottom Of List message at bottom }
MaxDetailLines := Pred(MaxDetailLines); { Leave space for message }
If InCount = 22 Then
MaxDetailLines := 21; { Don't write all 22 lines without message }
Row := 1; { For procedure MemoryWrite }
Col := 1;
Write(' Seq.',Heading,#13,#13); { Heading, CR, and & blank line }
If TopOfList Then
Write(' ***** Top Of List *****',#13);
Count := 0;
While (Count < MaxDetailLines) And (PagePointer + Count < InCount) Do
Begin { While loop to write a screenful of object lines }
Count := Succ(Count); { Index for SelectArray }
Object := SelectArray[PagePointer + Count]; { Get object to list }
Write(' ',PagePointer + Count:4); { Sequence # for listing }
WriteALine; { Write object to Con: device }
End; { While }
If BottomOfList Then
Write(' ***** Bottom Of List *****',#13);
End; { Procedure WriteScreen }
Begin { Procedure View }
If NewSelection Then
Sort; { User cannot view data until it is sorted }
ConOutPtr := Ofs(MemoryWrite); { Activate screen output driver }
Assign(Device,'Con:'); { So WriteALine will write to the screen }
Reset(Device);
FunKey := False; { A cursor control key has not been pressed }
Window(1,1,80,25); { Set window size to entire screen }
If InCount > 0 Then { There is something selected to view }
Begin { Then }
PagePointer := 0; { Index to top of SelectArray }
WriteScreen; { Write initial screenful of output }
Repeat { Accept keyboard input until user "q"uits }
Repeat
Read(Kbd,Ch);
FunKey := (Ch = Escape) And KeyPressed;
Until FunKey Or (Upcase(Ch) In ['Q','B','E','S']);
If FunKey And (InCount > 21) Then { Respond to edit keys }
Begin { Then }
FunKey := False; { Done pressing function key }
Read(Kbd,Ch); { Get 2nd character of code }
Case Ch Of
#71 : Begin { Case home }
PagePointer := 0; { Top of list }
WriteScreen;
End; { Case home }
#79 : Begin { Case end }
PagePointer := (InCount - 22); { Bottom of list }
If PagePointer < 0 Then
PagePointer := 0;
If InCount = 22 Then { Special case for TOL message }
PagePointer := 1;
WriteScreen;
End; { Case end }
#73 : Begin { Case page up }
PagePointer := PagePointer - 23; { Up in list }
If PagePointer < 0 Then { Exceeded top of list }
PagePointer := 0; { Top }
WriteScreen;
End; { Case page up }
#81 : Begin { Case page down }
Ch := 'a'; { # 81 is a 'Q' and we don't want to quit }
PagePointer := PagePointer + MaxDetailLines; { Down }
If PagePointer > (InCount - 22) Then { Exceeded list }
Begin { Then }
PagePointer := (InCount - 22); { Bottom }
If PagePointer < 0 Then
PagePointer := 0;
End; { Then }
If InCount = 22 Then
PagePointer := 1;
WriteScreen;
End; { Case page down }
End; { Case }
End { Then }
Else
If Ch = #81 Then
Ch := 'a'; { Page down returns a 'Q' and we don't want to quit }
If (Upcase(Ch) In ['B','E','S']) And (InCount > 21) Then
Begin { Then } { Skip halfway to beginning or end, }
Case Ch Of { or skip to a particular sequence }
'B','b' : PagePointer := PagePointer Div 2;
'E','e' : Begin { Case E }
PagePointer := PagePointer +
(InCount - PagePointer) Div 2;
If PagePointer > (InCount - 21) Then
Begin { Then }
PagePointer := (InCount - 21);
If PagePointer < 0 Then
PagePointer := 0;
End; { Then }
End; { Case E }
'S','s' : Begin { Case S }
{ Restore standard screen output driver }
ConOutPtr := AuxOutPtr;
ClrScr;
Repeat { 2 repeat loops to get valid seek # }
Repeat
Write('Enter sequence number between 1 and ');
Write(InCount,' to seek: ');
{$I-} Readln(PagePointer); {$I+}
OK := IOResult = 0;
If Not OK Then
Write(^G); { Beep to indicate entry error }
Writeln;
Until OK;
AllOK := (PagePointer >= 1) And
(PagePointer <= InCount);
PagePointer := Pred(PagePointer);
If Not AllOK Then
Write(^G); { Beep to indicate entry error }
Until AllOK;
If PagePointer > (InCount - 21) Then
Begin { Then }
PagePointer := (InCount - 21);
If PagePointer < 0 Then
PagePointer := 0;
End; { Then }
{ Go back to IO driver for view screen }
ConOutPtr := Ofs(MemoryWrite);
End; { Case S }
End; { Case }
WriteScreen; { After "B","E", or "S" entry }
End; { Then }
Until Upcase(Ch) = 'Q'; { Until user quits the viewer }
End { Then }
Else
Begin { Else }
Row := 19; { Row & col for printing error message }
Col := 10;
Write('No objects to view - Press Space To Continue ');
WaitForSpace;
ClrScr; { Clear the larger view window before returning to main menu }
End; { Else }
ConOutPtr := AuxOutPtr; { Restore standard screen output driver }
End; { Procedure View }
Procedure Terminate;
{ This procedure is called when the user Quits the program }
Var
ActiveTime,ActiveHours,ActiveMinutes,ActiveSeconds : Real;
Begin { Procedure Terminate }
Window(1,1,80,25); { Restore full screen window }
ClrScr; { Leave the DOS level screen uncluttered except for final message }
FinishTime := Time; { Used To determine program run time }
If FinishTime < StartTime Then
FinishTime := FinishTime + 86400.0; { Add 24 hours after midnight }
ActiveTime := FinishTime - StartTime; { Program run time in seconds }
ActiveHours := Int(ActiveTime / 3600);
ActiveMinutes := Int((ActiveTime - ActiveHours * 3600) / 60);
ActiveSeconds := ActiveTime - ActiveHours * 3600 - ActiveMinutes * 60;
Write('Hbase active for ');
If ActiveHours > 0 Then
If ActiveHours > 1 Then
Write(ActiveHours:2:0,' hours ')
Else
Write(ActiveHours:2:0,' hour ');
If ActiveMinutes > 0 Then
If ActiveMinutes > 1 Then
Write(ActiveMinutes:2:0,' minutes ')
Else
Write(ActiveMinutes:2:0,' minute ');
If ActiveSeconds > 1 Then
Write(ActiveSeconds:2:0,' seconds')
Else
Write(ActiveSeconds:2:0,' second');
Writeln(' - returning to DOS ...');
End; { Procedure Terminate }
{$I SELECTS.INC} { Include parameter selection procedures }
Procedure Precess;
{ This procedure precesses the selected data's celestial coordinates to
another epoch. The algolrithm is taken from Eric Burgess' CELESTIAL BASIC,
and it is not as accurate as I would like. If you improve on it (even at a
loss of speed), please let me know, 'cause I could use it myself. }
Var
R1,D1,T2,ChangeInRA,ChangeInDec,NewEpoch,
RealMins,Difference,X,Y,Z,LastYear : Real;
Function Tan (AngleInDegrees : Real): Real;
{ Represents the tangent of its degree-valued argument }
Var
Angle : Real;
Function ConvertToRadians(Angle : Real): Real;
Begin { Function ConvertToRadians }
ConvertToRadians := Angle * (Pi / 180);
End; { Function ConvertToRadians }
Begin { Function Tan }
Angle := ConvertToRadians(AngleInDegrees);
Tan := Sin(Angle) / Cos(Angle);
End; { Function Tan }
Begin { Procedure Precess }
If NewSelection Then
Sort; { User cannot precess data until it is sorted }
If Selected And (InCount > 0) Then
Begin { Then }
ClrScr;
LastYear := CurrentEpoch;
Repeat
Write('Enter the new epoch: ');
{$I-} Readln(NewEpoch) {$I+};
OK := (IOResult = 0);
If Not OK Then
Write(^G); { Ring bell to alert user to entry error }
Writeln;
Until OK;
ClrScr;
Writeln('Selected data being precessed to epoch ',NewEpoch:7:2);
Difference := NewEpoch - LastYear;
CurrentEpoch := NewEpoch;
For Index := 1 To InCount Do
Begin { For }
Object := SelectArray[Index]; { Get next object to precess }
With Object Do { Precess it }
Begin { With }
R1 := RAHrs + RAMins / 60 + RASecs / 3600;
D1 := DecDeg + DecMin / 60;
R1 := R1 * 15;
T2 := ((LastYear + NewEpoch) / 2 - 1900) / 100;
X := 3.07234 + (0.00186 * T2);
Y := 20.0468 - (0.0085 * T2);
Z := Y / 15;
ChangeInRA := 0.0042 * Difference *
(X + (Z * Sin(R1/57.29878) * Tan(D1/57.29878)));
R1 := R1 + ChangeInRA;
D1 := D1 + 0.00028 * Difference * Y * Cos(R1 / 57.29878);
R1 := R1 / 15;
If R1 > 24 Then
R1 := R1 - 24;
If R1 < 0 Then
R1 := R1 + 24;
RAHrs := Trunc(Int(R1));
RealMins := (60 * (R1 - Int(R1)));
RASecs := Trunc(60 * (RealMins - Int(RealMins)));
RAMins := Trunc(RealMins);
If D1 > 90 Then
D1 := 90 - (D1 - Int(D1));
DecDeg := Trunc(Int(D1));
DecMin := Trunc((D1 - Int(D1)) * 60);
If D1 < 0 Then
Begin { Then }
DecDeg := Trunc(Int(D1));
D1 := Abs(D1);
DecMin := Trunc(60 * (D1 - Int(D1)));
End; { Then }
End; { With }
SelectArray[Index] := Object; { Put precessed object back }
End; { For }
End { Then }
Else
Begin { Else }
If InCount > 0 Then { Selected = False }
Writeln('No data has been selected for precession')
Else { Incount = 0 }
Writeln(' No objects to precess');
Write (' Press space to continue ');
WaitForSpace;
End; { Else }
End; { Procedure Precess }
Procedure ExamineStatus; { Show selected values to user }
Var
Index,ConCount : Byte;
ConSelected : Boolean;
Begin { Procedure ExamineStatus }
ClrScr;
Window(20,1,80,25); { Center status display screen }
Writeln;
ConCount := 0;
ConSelected := False;
HighVideo; Writeln('Current selected values are:'); LowVideo;
Writeln;
Write('Sorted by ');
Case SortField Of
'H','h',' ' : Writeln('Herschel class.');
'N','n' : Writeln('NGC number.');
'R','r' : Writeln('right ascension.');
'D','d' : Writeln('declination.');
'M','m' : Writeln('magnitude.');
'O','o' : Writeln('object type.');
'C','c' : Writeln('constellation.');
End; { Case }
Writeln;
If ClassSet >= [1..8] Then
Writeln('All Herschel classes.')
Else
Begin { Else }
Write('Herschel class(es) ');
For Index := 1 To 8 Do
If Index In ClassSet Then
Write(ClassNames[Index],' ');
Writeln;
End; { Else }
Writeln;
If (LowNGC <= 0) And (HighNGC >= 8000) Then
Writeln('All NGC numbers.')
Else
Writeln('NGC numbers from ',LowNGC,' to ',HighNGC);
Writeln;
If (LowRAHr <= 0) And (LowRAMin <= 0) And (HighRAHr >= 24)
And (HighRAMin >= 60) Then
Writeln('All r.a. values.')
Else
Writeln('R.A. from ',LowRAHr,' Hrs, ',LowRAMin,' Min to ',
HighRAHr,' Hrs, ',HighRAMin,' Min.');
Writeln;
If (LowDecDeg <= -90) And (LowDecMin <= -60) And (HighDecDeg >= 90) And
(HighDecMin >= 60) Then
Writeln('All Dec. values.')
Else
Writeln('Dec. from ',LowDecDeg,' Deg, ',LowDecMin,' Min to ',
HighDecDeg,' Deg, ',HighDecMin,' Min.');
Writeln;
If (LowMag <= 0) And (HighMag >= 170.0) Then
Writeln('All magnitudes.')
Else
Writeln('Magnitudes from ',(LowMag/10):4:1,' to ',(HighMag/10):4:1,'.');
Writeln;
If TypeSet >= [1..7] Then
Writeln('All object types.')
Else
Begin { Else }
Write('Object type(s) ');
For Index := 1 To 7 Do
If Index In TypeSet Then
Write(ObjectTypes[Index],' ');
Writeln;
End; { Else }
Writeln;
For Index := 1 To NumberOfConstellations Do
If Not Constel[Index] Then
Begin { Then }
ConSelected := True;
ConCount := Succ(ConCount);
End; { Then }
If Not ConSelected Then
Writeln('All Constellations.')
Else
If ConCount = NumberOfConstellations Then
Writeln('No Constellations.')
Else
Begin { Else }
Writeln('The following constellation(s):');
ConCount := 0;
For Index := 1 To NumberOfConstellations Do
Begin { For }
If Constel[Index] Then
Begin { Then }
ConCount := Succ(ConCount);
If ConCount > 14 Then
Begin { Then }
Writeln;
ConCount := 0;
End { Then }
Else
Write(Names[Index],' ');
End; { Then }
End; { For }
End; { Else }
Writeln; Writeln;
HighVideo; Write('Press space to return to main menu '); LowVideo;
WaitForSpace;
End; { Procedure ExamineStatus }
{$I HELP.INC} { Include the online help procedure }
Procedure MainMenu;
{ This is the main menu called by the main program }
Begin { Procedure MainMenu }
ClrScr;
Window(11,1,80,25); { Center the main menu screen }
LowVideo; { Some procedures return in HighVideo mode }
Writeln;
If InCount <> 1 Then { Test to keep our grammar correct }
Writeln('There are currently ',InCount,' objects selected.')
Else
Writeln('There is currently 1 object selected.');
Writeln;
HighVideo;
If NewSelection Then { New selections not yet sorted - warn the user }
Begin { Then }
Writeln('New selections have not been sorted.');
Writeln;
End; { Then }
If Expanding Then { Notify the user }
Begin { Then }
Writeln('Selections are being expanded.');
Writeln;
End; { Then }
LowVideo;
Writeln('You can select a sub-listing by:');
Writeln;
HighVideo; Write(' H'); LowVideo; Writeln('erschel Class');
HighVideo; Write(' N'); LowVideo; Writeln('GC Number');
HighVideo; Write(' R'); LowVideo; Writeln('ight Ascension');
HighVideo; Write(' D'); LowVideo; Writeln('eclination');
HighVideo; Write(' M'); LowVideo; Writeln('agnitude');
HighVideo; Write(' O'); LowVideo; Writeln('bject Type');
HighVideo; Write(' C'); LowVideo; Writeln('onstellation');
Writeln;
Write('Type a letter to select, or to ');
HighVideo; Write('S'); LowVideo; Write('ort, ');
HighVideo; Write('T'); LowVideo; Writeln('oggle expansion, ');
HighVideo; Write('E'); LowVideo; Write('xamine status, ');
HighVideo; Write('I'); LowVideo; Write('nitialize, ');
HighVideo; Write('V'); LowVideo; Write('iew, ');
HighVideo; Write('L'); LowVideo; Write('ist, ');
HighVideo; Write('P'); LowVideo; Write('recess, or ');
HighVideo; Write('Q'); LowVideo; Writeln('uit.');
Writeln;
Write('You may type '); HighVideo; Write('F1 '); LowVideo;
Writeln('for help.');
Writeln;
Write('Your Choice? ');
Repeat
Read(Kbd,Ch);
Until Upcase(Ch) In ['H','N','R','D','M','C','O','E',
#27,'T','I','S','V','L','P','Q'];
HighVideo; Writeln(Ch); LowVideo;
Writeln;
Case Ch Of
#27 : Begin { Check for PF1 (help) else ignore extended code keys }
If Keypressed Then
Read(Kbd,Ch); { Get 2nd character of extended scan code }
If Ch = #59 Then { PF1 was pressed }
MainMenuHelp
Else
Ch := ' '; { Space out unwanted 2nd character }
End; { Case escape }
'H','h' : SelectH;
'N','n' : SelectNGC;
'R','r' : SelectRA;
'D','d' : SelectDec;
'M','m' : SelectMag;
'O','o' : SelectType;
'C','c' : SelectCon;
'E','e' : ExamineStatus;
'I','i' : InitializeVariables;
'L','l' : List;
'P','p' : Precess;
'V','v' : View;
'S','s' : Sort;
'Q','q' : Begin { Case Q }
Write('Exit to DOS? (Y/N): ');
Repeat { Loop to catch those damn escape codes }
Repeat
Read(Kbd,Ch);
Until Upcase(Ch) In ['Y','N',#27];
If (Ch = #27) And Keypressed Then { Escape code pressed }
Begin { Then }
Read(Kbd,Ch); { Get 2nd char of escape code }
Ch := 'a'; { Weed out unwanted "y"s & "n"s }
End; { Then }
Until Upcase(Ch) In ['Y','N'];
Writeln(Ch);
Done := Upcase(Ch) = 'Y';
End; { Case Q }
'T','t' : Begin { Case T }
Expanding := Not Expanding;
Selected := False;
End; { Case T }
End; { Case }
End; { Procedure MainMenu }
Begin { Program }
Initialize;
While Not Done Do
MainMenu;
Terminate;
End. { Program }